#!/usr/bin/env python
# -*- coding: UTF-8 -*-

# local_contact_store.py 
#
#  Copyright (c) 2007 INdT (Instituto Nokia de Technologia)
#
#  Author: Kenneth Rohde Christiansen <kenneth.christiansen@gmail.com>
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License as
#  published by the Free Software Foundation; either version 2 of the
#  License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
#  USA

import pygtk
pygtk.require('2.0')
import gtk
import gobject
import tapioca
import os
import icon_theme
import util
import shutil

class LocalContactStore (gobject.GObject):

    __gsignals__ = {
        'contact-store-updated' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_PYOBJECT,)),
        'all-contact-store-updated' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_PYOBJECT,)),
    }

    def __init__ (self):
        gobject.GObject.__init__(self)

        self.contact_list = None
        self.alias = {}
        self.local_alias = {}
        self.presence = {}
        self.presence_message = {}
        self.avatar_token = {}
        self.avatar_pixbuf = {}
        self.avatar_thumbnail = {}
        self.contact = {}
        self.blocked_contacts = {}

        # pixbufs
        self.presence_pixbuf = {}
        self.blocked_icon = None
        self.remote_pending_icon = None
        self.default_avatar_pixbuf = None
        self.default_avatar_thumbnail = None

        self.ereseva_dir = os.path.expanduser(os.environ.get('HOME')) + '/.ereseva'
        self.user_avatar_dir = self.ereseva_dir + '/avatars'
        self.contact_avatar_dir = self.ereseva_dir + '/avatars/contacts'
        self.avatars_filename = self.contact_avatar_dir + '/avatars.txt'

        if not os.path.exists(self.ereseva_dir):
            os.makedirs(self.ereseva_dir)

        if not os.path.exists(self.user_avatar_dir):
            os.makedirs(self.user_avatar_dir)

            try:
                path = "/usr/share/ereseva/avatars"
                files = os.walk(path).next()[2]

                print '-------======================='
                print '-------======================='
                print '-------======================='

                for filename in files:
                    if not filename.endswith("png"):
                        continue

                    shutil.copy(path + "/" + filename, \
                    self.user_avatar_dir + "/" + filename)

            except Exception, e:
                pass

        if not os.path.exists(self.contact_avatar_dir):
            os.makedirs(self.contact_avatar_dir)

        self._load_pixbufs()
        self._load_avatar_info()


    def clear(self):
        self.contact_list = None
        self.alias = {}
        self.local_alias = {}
        self.presence = {}
        self.presence_message = {}
        self.avatar_token = {}
        self.avatar_pixbuf = {}
        self.avatar_thumbnail = {}
        self.contact = {}
        self.blocked_contacts = {}


    def _load_pixbufs(self):
        '''
        Loads presence, cached and default avatars images as pixbufs.
        '''

        theme = icon_theme.icon_theme_get_default()
        missing_icon = theme.load_icon('gtk-missing-image', 16, 0)
        self.presence_pixbuf = {}

        try:
            icon = theme.load_icon('status-online', 16, 0)
        except Exception, e:
            icon = missing_icon
        self.presence_pixbuf[tapioca.PRESENCE_AVAILABLE] = icon

        try:
            icon = theme.load_icon('status-away', 16, 0)
        except Exception, e:
            icon = missing_icon
        self.presence_pixbuf[tapioca.PRESENCE_AWAY] = icon

        try:
            icon = theme.load_icon('status-away', 16, 0)
        except Exception, e:
            icon = missing_icon
        self.presence_pixbuf[tapioca.PRESENCE_XA] = icon

        try:
            icon = theme.load_icon('status-busy', 16, 0)
        except Exception, e:
            icon = missing_icon
        self.presence_pixbuf[tapioca.PRESENCE_BUSY] = icon

        try:
            icon = theme.load_icon('status-offline', 16, 0)
        except Exception, e:
            icon = missing_icon
        self.presence_pixbuf[tapioca.PRESENCE_OFFLINE] = icon

        try:
            icon = theme.load_icon('status-offline', 16, 0)
        except Exception, e:
            icon = missing_icon
        self.presence_pixbuf[tapioca.PRESENCE_HIDDEN] = icon

        self.blocked_icon = theme.load_icon('status-blocked', 16, 0)
        self.remote_pending_icon = theme.load_icon('status-pending', 16, 0)
        self.default_avatar_pixbuf = theme.load_icon('avatar-unknown', 96, 0)
        self.default_avatar_thumbnail = theme.load_icon('avatar-unknown', 48, 0).\
            scale_simple(int(32), int(32), gtk.gdk.INTERP_BILINEAR)


    def send_store_updated(self, contact_id):
        '''
        Emits a signal that the local contact store is updated for a given id.
        '''
        self.emit('contact-store-updated', contact_id)


    def send_all_store_updated(self):
        '''
        Emits a signal that the local contact store is updated for all ids.
        '''

        self.emit('all-contact-store-updated', self.get_all_ids())


    def get_all_ids(self):
        '''
        Returns all ids in the store.
        '''

        ids = self.contact.keys()
        ids.sort()

        return ids


    def get_pending_contacts(self):
        '''
        Returns all ids in the store with pending authorization status.
        '''

        pending = []
        for contact in self.get_all_contacts().values():
            try:
                if contact.get_authorization_status() == \
                    tapioca.AUTHORIZATION_STATUS_LOCAL_PENDING:
                        pending.append(contact.get_uri())
            except Exception, e:
                pass

        return pending


    def get_contact_list(self):
        return self.contact_list


    def get_all_contacts(self):
        return self.contact


    def get_contact(self, contact_id):
        '''
        Returns the contact for a given id.
        '''

        return self.contact[contact_id]


    def get_alias(self, contact_id, local=False):
        '''
        Returns the alias for a given id.
        '''

        try:
            if local:
                alias = self.local_alias[contact_id]
            else:
                alias = self.alias[contact_id]
        except KeyError, e:
            alias = contact_id

        return alias


    def get_presence (self, contact_id):
        '''
        Returns the Tapioca.Presence for a given id.
        '''

        return self.presence[contact_id]


    def get_presence_icon (self, contact_id):
        '''
        Returns the presence icon as a gtk.Pixbuf for a given id.
        '''

        if self.is_blocked(contact_id):
            return self.blocked_icon
        elif self.is_pending(contact_id):
            return self.remote_pending_icon
        else:
            return self.presence_pixbuf[self.presence[contact_id]]


    def get_presence_icon_from_presence (self, presence):
        '''
        Returns the presence icon as a gtk.Pixbuf for a given presence.
        '''

        return self.presence_pixbuf[presence]


    def get_presence_message (self, contact_id):
        '''
        Returns a presence message, custom or the current presence as text for
        a given id.
        '''

        msg = self.get_custom_presence_message_only(contact_id)

        if msg != None and msg != "":
            return msg

        presence = self.presence[contact_id]
        msg = self.get_presence_message_from_presence(presence)

        if self.is_pending(contact_id):
            msg += ' - Authorization Pending'

        return msg


    def get_presence_message_from_presence(self, presence):
        '''
        Returns a generic presence message associated with the given presence.
        '''

        if presence == tapioca.PRESENCE_AVAILABLE:
            msg = 'Available'
        elif presence == tapioca.PRESENCE_AWAY:
            msg = 'Away'
        elif presence == tapioca.PRESENCE_XA:
            msg = 'Idle'
        elif presence == tapioca.PRESENCE_BUSY:
            msg = 'Busy'
        else:
            msg = 'Offline'

        return msg


    def get_custom_presence_message_only (self, contact_id):
        '''
        Returns a custom presence message if available, or else None,
        for a given id.
        '''

        msg = self.presence_message[contact_id]

        if msg != None:
            return str(msg)
        else:
            return None


    def get_presence_information (self, contact_id):
        '''
        Returns a presence information string or None.
        '''

        presence = self.presence[contact_id]
        alias = self.get_alias(contact_id)

        if presence == tapioca.PRESENCE_OFFLINE:
            presence_information = "<small>%s is offline at the moment, " \
                    "messages you send will be delivered when %s " \
                    "comes online.</small>" % (alias, alias)
        elif presence == tapioca.PRESENCE_BUSY:
            presence_information = "<small>%s is busy. You may be " \
                    "interrupting.</small>" % alias
        else:
            presence_information = None

        return presence_information


    def get_presence_priority(self, contact_id):
        presence = self.presence[contact_id]
        priority = 1

        if presence == tapioca.PRESENCE_AVAILABLE:
            priority = 1 
        elif presence == tapioca.PRESENCE_AWAY:
            priority = 3
        elif presence == tapioca.PRESENCE_XA:
            priority = 3
        elif presence == tapioca.PRESENCE_BUSY:
            priority = 2
        else:
            #if contact.is_blocked():
            #    presence_image = presence_image_blocked
            #    priority = 4
            #else:
            ## contact offline
            priority = 5

        return priority


    def get_avatar_token(self, contact_id):
        """
        Returns the avatar token (hash of avatar) for a given id.
        """

        try:
            return self.avatar_token[contact_id]
        except KeyError:
            return None


    def get_avatar_pixbuf (self, contact_id):
        """
        Returns the avatar (96x96) as a gdk.Pixbuf for a given id.
        """

        try:
            return self.avatar_pixbuf[contact_id]
        except KeyError:
            return self.default_avatar_pixbuf


    def get_avatar_thumbnail (self, contact_id):
        """
        Returns the avatar thumbnail (32x32) as a gdk.Pixbuf for a given id.
        """

        try:
            thumbnail = self.avatar_thumbnail[contact_id]
        except KeyError:
            thumbnail = self.default_avatar_thumbnail

        try:
            if self.presence[contact_id] == tapioca.PRESENCE_OFFLINE:
                temp_avatar = thumbnail.copy()
                thumbnail.saturate_and_pixelate(temp_avatar, 0.0, False)

                return temp_avatar
            else:
                return thumbnail
        except KeyError:
            return thumbnail


    def get_local_alias (self, contact_id):
        return self.alias[contact_id]


    def is_blocked(self, contact_id):
        """
        Function that tells whether a contact is blocked 
        or not. If information cannot be found, the contact
        is assumed not to be blocked.
        """

        return self.blocked_contacts.has_key(contact_id)


    def is_pending(self, contact_id):
        """
        Function that tells wheter a contact is waiting for
        authorization from the user or not. If information
        cannot be found, pending is assumed.
        """

        if self.contact.has_key(contact_id):
            contact = self.contact[contact_id]
        else:
            return False

        try:
            is_pending = \
                (contact.get_authorization_status() == \
                tapioca.AUTHORIZATION_STATUS_LOCAL_PENDING)
        except KeyError, e:
            return True
        except AttributeError, e:
            print '[local_contact_store.py:is_pending]'
            print e
            return False

        return is_pending


    def set_contact_list(self, contact_list):
        self.contact_list = contact_list


    def set_contact(self, contact_id, contact):
        '''
        Sets the Tapioca.Contact for a given id. To be called from Tapioca
        update callbacks.
        '''

        self.contact[contact_id] = contact


    def set_alias (self, contact_id, alias):
        '''
        Sets the alias for a given id. To be called from Tapioca update
        callbacks.
        '''

        self.alias[contact_id] = alias


    def set_local_alias (self, contact_id, local_alias):
        '''
        Description...
        '''

        self.local_alias[contact_id] = local_alias


    def set_presence (self, contact_id, presence):
        '''
        Sets the Tapioca.Presence for a given id. To be called from 
        Tapioca update callbacks.
        '''

        self.presence[contact_id] = presence


    def set_presence_message (self, contact_id, message):
        '''
        Sets the presence message for a given id. To be called 
        from Tapioca update callbacks.
        '''

        self.presence_message[contact_id] = message


    def set_avatar (self, contact_id, avatar, token):
        '''
        Sets the avatar and associated token for a given id. To
        be called from Tapioca update callbacks.

        The funcion generates the right-sized avatar pixbuf (96x96)
        and a thumbnail version (32x32) for use in the contact list.
        '''

        if token == None:
            return

        avatar_mime = avatar.get_mime_type()

        if avatar_mime == None:
            return
        try:
            avatar_pixbuf = util.pixbuf_from_avatar(avatar)
        except gobject.GError, e:
            return

        def _scale(pixbuf, desired_width, desired_height):
            if avatar_width > avatar_height:
                desired_height = avatar_height * (desired_width / avatar_width)
            else:
                desired_width = avatar_width * (desired_height / avatar_height)

            scaled_avatar = pixbuf.scale_simple(int(desired_width), \
                    int(desired_height), gtk.gdk.INTERP_BILINEAR)

            return scaled_avatar

        avatar_width = avatar_pixbuf.get_width() + 0.0
        avatar_height = avatar_pixbuf.get_height() + 0.0

        # check if size is equal to 96x96! if not! rescale!
        if (avatar_width != 96.0) or (avatar_height != 96.0):
            original_avatar_pixbuf = _scale(avatar_pixbuf, 96.0, 96.0)
        else:
            original_avatar_pixbuf = avatar_pixbuf

        # Create the avatar thumbnail
        thumbnail_avatar_pixbuf = _scale(avatar_pixbuf, 32.0, 32.0)

        original_avatar_filename = \
                self.contact_avatar_dir + '/' + token + '.original.avatar'
        thumbnail_avatar_filename = \
                self.contact_avatar_dir + '/' + token + '.online.avatar'

        original_avatar_pixbuf.save(original_avatar_filename, 'png')
        thumbnail_avatar_pixbuf.save(thumbnail_avatar_filename, 'png')

        # Update the dictionaries
        '''
        print '---------------------------------------'
        print '[set_avatar]'
        print contact_id,
        print ' - ',
        print token
        print original_avatar_filename
        print thumbnail_avatar_filename
        '''
        if os.path.isfile(original_avatar_filename) \
            and os.path.isfile(thumbnail_avatar_filename):
            self.avatar_token[contact_id] = token
            self.avatar_pixbuf[contact_id] = original_avatar_pixbuf
            self.avatar_thumbnail[contact_id] = thumbnail_avatar_pixbuf

            # Update the avatars.txt file
            self._save_avatar_info()


    def set_local_alias (self, contact_id, alias):
        self.local_alias[contact_id] = alias


    def remove_contact(self, contact_id):
        if self.contact.has_key(contact_id):
            del self.contact[contact_id]


    def block(self, contact_id, do_block=True):
        if do_block:
            self.blocked_contacts[contact_id] = True
        elif self.blocked_contacts.has_key(contact_id):
            self.blocked_contacts.pop(contact_id)


    def _load_avatar_info(self):
        """
        Load cached avatars.

        Read text file avatars.txt into list
        avatars.txt data are in the form: user_id<space>avatar_token

        Try opening $TOKEN$.original.avatar and $TOKEN$.online.avatar
        (thumbnail) for each entry (id)

        Set the avatar and the avatar thumbnail and the token for the id
        - if non-existant, do not set
        """

        if not os.path.isfile(self.avatars_filename):
            return

        avatars_file = open(self.avatars_filename, 'r')

        for line in avatars_file.readlines():
            contact_id, token = line.strip().split(' ')

            original_avatar = self.contact_avatar_dir + '/' + token + '.original.avatar'
            thumbnail_avatar = self.contact_avatar_dir + '/' + token + '.online.avatar'

            if os.path.isfile(original_avatar) and os.path.isfile(thumbnail_avatar):
                self.avatar_token[contact_id] = token
                self.avatar_pixbuf[contact_id] = gtk.gdk.pixbuf_new_from_file(original_avatar)
                self.avatar_thumbnail[contact_id] = gtk.gdk.pixbuf_new_from_file(thumbnail_avatar)

        avatars_file.close()


    def _save_avatar_info(self):
        avatars_file = open(self.avatars_filename, 'w')

        for contact_id in self.avatar_token.keys():
            avatars_file.write(contact_id + ' ' + self.avatar_token[contact_id] + "\n")

        avatars_file.close()

